#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

/* Array de clientes. */
int clientes[3];

void *conexion(void *);

int main(int argc , char *argv[])
{
    /* Descriptores para la creación de sockets. */
    int socket_desc, fd_accept, addrlen, *new_sock;
    
    /* Sockets para cliente y servidor. */
    struct sockaddr_in servidor, cliente;
    char *message;

    /* Creamos un socket. */
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        printf("Error al crear el socket.");
    }

    /* Seteamos los parámetros para el servidor. */
    servidor.sin_family = AF_INET;
    servidor.sin_addr.s_addr = INADDR_ANY;
    servidor.sin_port = htons( 8888 );

    /*
     * int bind(int fd, struct sockaddr *my_addr,int sizeof(struct sockaddr));
     * Recibe como parámetros el descriptor utilizado en la función socket,
     * un puntero a una estructura sockaddr
     * y la longitud de la estructura sockaddr a la que apunta el puntero anterior.
     * Esta función asocia un socket con un puerto de la máquina (en este caso
     * será el puerto indicado en las líneas anteriores).
    */
    if( bind(socket_desc, (struct sockaddr *)&servidor, sizeof(servidor)) < 0)
    {
        puts("La asociación ha fallado.");
        return 1;
    }
    puts("Asociación establecida.");

    /* int listen(int fd,int backlog);
     * Recibe como parámetros el descriptor utilizado en la función socket
     * y el número de conexiones permitidas.
     * Se usa para esperar conexiones, es decir, se usa para cuando
     * cuando alguien desea conectarse a nuestra máquina.
    */
    listen(socket_desc , 3);

    puts("Esperando por conexiones...");
    /* Se determina la longitud de la estructura sockaddr_in para usarla en "accept".*/
    addrlen = sizeof(struct sockaddr_in);

    /*
     * int accept(int fd, void *addr, int *addrlen);
     * Recibe como parámetros el descriptor utilizado en la función listen,
     * un puntero a una estructura sockaddr_in en la que se pueda determinar qué nodo se está conectando y desde qué puerto
     * y la longitud de la estuctura a la que apunta el segundo parámetro.
     * Se usa para aceptar conexiones entrantes.
    */
    int i = 0;
    for( i = 0; i < 3; i++ )
        clientes[i] = 0;
    i = 0;
    
    while( (fd_accept = accept(socket_desc, (struct sockaddr *)&cliente, (socklen_t*)&addrlen)) )
    {
        puts("Conexión aceptada.");

        pthread_t thread_cliente;
        new_sock = malloc(1);
        *new_sock = fd_accept;
        
        /**/
        clientes[i] = fd_accept;
        printf("%d\n",clientes[i]);
        i++;
        
        /**/

        if( pthread_create( &thread_cliente , NULL ,  conexion , (void*) new_sock) < 0)
        {
            perror("Error al crear la hebra.");
            return 1;
        }

        //Now join the thread , so that we dont terminate before the thread
        //pthread_join( sniffer_thread , NULL);
    }

    //pthread_join( sniffer_thread , NULL);
    if (fd_accept < 0)
    {
        perror("Falló la aceptación.");
        return 1;
    }
    return 0;
}

/*
 * Este método manipula las conexiones
 * para cada cliente que desee participar en el chat.
 * */
void *conexion(void *socket_desc)
{
    /* Se obtiene el descriptor. */
    int sock = *(int*)socket_desc;
    int read_size;
    char *message , client_message[2000];

    int i;
    int tamano = sizeof(clientes)/sizeof(clientes[0]);
    //Receive a message from client
    
    while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
    {
        //Send the message back to client
        for(  i = 0; i < tamano; i++ ){
            printf("%d : este es el printf de la hebra\n",clientes[i]);
            if( clientes[i] > 0 ){
                printf("Entré a escribir al descriptor %d\n",clientes[i]);
                write(clientes[i] , client_message , strlen(client_message));
            }
        }
    }

    if(read_size == 0)
    {
        puts("Client disconnected");
        fflush(stdout);
    }
    else if(read_size == -1)
    {
        perror("recv failed");
    }
    //Free the socket pointer
    free(socket_desc);

    return 0;
}
